Skip to content

fix: lmp.Gaussian potential from its analytic deflection (correct + ~100x faster) - #681

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/lmp-gaussian-potential-fast
Oct 29, 2026
Merged

Jammy2211 merged 1 commit into
mainfrom
feature/lmp-gaussian-potential-fast

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Closes #680.

What was wrong

lmp.Gaussian.potential_2d_from (and so lmp.GaussianGradient, every MGE stellar Basis) decomposed one Gaussian into 100 Gaussians and integrated their deflections. It was slow — a 40-member basis stalled fit visualisation (subplot_tracer potential panel, tracer.fits) for >10 min on the NumPy path (LensTwin phase 4, Euclid DR1 twins) — and wrong: its finite-difference gradient did not reproduce the profile's own deflections (O(1) differences, sign errors for elliptical Gaussians).

Fix

A Gaussian is its own one-term MGE. The potential is now ψ(r) = ∫₀¹ α(t r)·r dt over MGEDecomposer's 20-node Gauss–Legendre rule, using the same in-frame analytic deflection as deflections_2d_via_analytic_from (factored into _deflections_in_frame_from, so the two cannot drift). One Faddeeva call per node.

Checks

  • ∇ψ = α to 1e-8 (circular and two elliptical cases); ∇²ψ = 2κ to ~1e-7 (circular 8e-5, same as the existing deflections).
  • 40-member basis on 100×100: ~2.4 s.
  • JAX vs NumPy: 5e-15.
  • Full suite: 1448 passed (no test pinned the old values); 4 new tests in test_gaussian.py.

Sersic / Chameleon / cored-Sersic potentials still use the MGE route (correctly — they are not Gaussians); not checked here.

🤖 Generated with Claude Code

…ection

The potential decomposed a single Gaussian into 100 Gaussians and was wrong:
its gradient did not reproduce the profile's deflections (sign errors for
elliptical Gaussians). It is now psi = int_0^1 alpha(t r).r dt over the
20-node Gauss-Legendre rule, using the same in-frame analytic deflection as
deflections_2d_via_analytic_from (one Faddeeva call per node). Gradient
matches the deflections to 1e-8 and lap(psi) = 2 kappa; a 40-member MGE
basis potential on 100x100 drops from >10 min to ~2 s.

Closes #680

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Jammy2211 Jammy2211 added the pending-release PR queued for the next release build label Oct 29, 2026
@Jammy2211
Jammy2211 merged commit 7efb52a into main Oct 29, 2026
4 checks passed
@Jammy2211
Jammy2211 deleted the feature/lmp-gaussian-potential-fast branch October 29, 2026 21:31
@pyautolabs-bot pyautolabs-bot Bot removed the pending-release PR queued for the next release build label Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf: lmp.Gaussian potential from its analytic deflection, not a 100-term MGE

1 participant